﻿using System;
using System.ComponentModel;
using System.Windows.Input;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Dynamic;

namespace NoMvvm.UnitTests
{
    [TestClass]
    public class ViewModelTests
    {
        private class TestViewModel : ViewModel
        {
            public TestViewModel()
                : base()
            { }

            public TestViewModel(object obj)
                : base(obj)
            { }

            public void MyMethod()
            { }

            public void MyMethodDynamic(dynamic a)
            { }

            public void MyMethodObject(object o)
            { }

            public void MyMethodThatWontMapToICommand(int a)
            { }

            public void MyMethodThatWontMapToICommand(int a, bool b, object c)
            { }
        }

        private class TestClass
        {
            public int Test { get; set; }
        }

        [TestMethod]
        public void Simple_Properties()
        {
            dynamic viewModel = new ViewModel();

            viewModel.MyString = "My String";
            viewModel.MyBool = true;

            Assert.AreEqual("My String", viewModel.MyString);
            Assert.AreEqual(true, viewModel.MyBool);
        }

        [TestMethod]
        public void Property_Changed()
        {
            dynamic viewModel = new ViewModel();
            bool changed = false;
            ((INotifyPropertyChanged)viewModel).PropertyChanged += (sender, e) => changed = e.PropertyName == "MyProperty";

            viewModel.MyProperty = 1234;

            Assert.IsTrue(changed);
        }

        [TestMethod]
        public void Action_T_To_ICommand()
        {
            dynamic viewModel = new ViewModel();

            viewModel.MyCommand = (Action<dynamic>)delegate { Console.WriteLine("Test"); };

            Assert.IsInstanceOfType(viewModel.MyCommand, typeof(ICommand));
        }

        [TestMethod]
        public void Action_To_ICommand()
        {
            dynamic viewModel = new ViewModel();

            viewModel.MyCommand = (Action)delegate { Console.WriteLine("Test"); };

            Assert.IsInstanceOfType(viewModel.MyCommand, typeof(ICommand));
        }

        [TestMethod]
        public void Action_To_ICommand_CanExecute()
        {
            dynamic viewModel = new ViewModel();

            viewModel.MyCommand = (Action)delegate { Console.WriteLine("Test"); };
            viewModel.CanMyCommand = true;

            Assert.IsTrue(((ICommand)viewModel.MyCommand).CanExecute(null));
        }

        [TestMethod]
        public void Action_To_ICommand_CanExecuteChanged()
        {
            dynamic viewModel = new ViewModel();
            viewModel.MyCommand = (Action)delegate { Console.WriteLine("Test"); };
            var command = ((ICommand)viewModel.MyCommand);
            bool changed = false;
            command.CanExecuteChanged += (sender, e) => changed = true;

            viewModel.CanMyCommand = true;
            
            Assert.IsTrue(changed);
        }

        [TestMethod]
        public void Reflect_Methods_T()
        {
            dynamic viewModel = new TestViewModel();

            Assert.IsInstanceOfType(viewModel.MyMethodDynamic, typeof(ICommand));
        }

        [TestMethod]
        public void Reflect_Methods_T_Object()
        {
            dynamic viewModel = new TestViewModel();

            Assert.IsInstanceOfType(viewModel.MyMethodObject, typeof(ICommand));
        }

        [TestMethod]
        public void Reflect_Methods()
        {
            dynamic viewModel = new TestViewModel();

            Assert.IsInstanceOfType(viewModel.MyMethod, typeof(ICommand));
        }

        [TestMethod]
        public void Reflect_Methods_Ctor()
        {
            dynamic viewModel = new TestViewModel(new { Test = 1 });

            Assert.IsInstanceOfType(viewModel.MyMethod, typeof(ICommand));
        }

        [TestMethod]
        public void Reflect_Methods_CanExecute()
        {
            dynamic viewModel = new TestViewModel();

            viewModel.CanMyMethod = true;

            Assert.IsTrue(((ICommand)viewModel.MyMethod).CanExecute(null));
        }

        [TestMethod]
        public void Reflect_Methods_CanExecuteChanged()
        {
            dynamic viewModel = new TestViewModel();
            var command = ((ICommand)viewModel.MyMethod);
            bool changed = false;

            command.CanExecuteChanged += (sender, e) => changed = true;
            viewModel.CanMyMethod = true;

            Assert.IsTrue(changed);
        }

        [TestMethod]
        public void Ctor_Test_Anon()
        {
            dynamic viewModel = new ViewModel(new { Test = 1 });

            Assert.AreEqual(1, viewModel.Test);
        }

        [TestMethod]
        public void Ctor_Test_Anon_Cached()
        {
            for (int i = 0; i < 1000; i++)
            {
                dynamic viewModel = new ViewModel(new { Test = 1 });
                Assert.AreEqual(1, viewModel.Test);
            }
        }

        [TestMethod]
        public void Ctor_Test_ViewModel()
        {
            dynamic viewModelIn = new ViewModel();
            viewModelIn.Test = 1;
            dynamic viewModel = new ViewModel(viewModelIn);

            Assert.AreEqual(1, viewModel.Test);
        }

        [TestMethod]
        public void Ctor_Test_Expando()
        {
            dynamic expando = new ExpandoObject();
            expando.Test = 1;
            dynamic viewModel = new ViewModel(expando);

            Assert.AreEqual(1, viewModel.Test);
        }

        [TestMethod]
        public void Ctor_Test_Class()
        {
            dynamic viewModel = new ViewModel(new TestClass { Test = 1 });

            Assert.AreEqual(1, viewModel.Test);
        }
    }
}
